﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Xml.Serialization;
using CharacterCreation.Data.Models;
using CharacterCreation.Views.Contracts;

namespace CharacterCreation.Data.Services {
    public class BaseService<T, U> : INotifyPropertyChanged
        where T : BasePersistanceClass, new()
        where U : new() {
        #region singleton
        protected static U _service;
        protected BaseService() { }

        /// <summary>
        /// Creates an instance of the character service
        /// </summary>
        /// <returns>An instance of the character service</returns>
        public static U CreateInstance() {
            if (_service == null)
                _service = new U();
            return _service;
        }
        #endregion

        /// <summary>
        /// Gets or sets the Characters
        /// </summary>
        public ObservableCollection<T> Items {
            get {
                if (_Items == null) {
                    _Items = DataContext.GetAll<T>(null);
                    RaisePropertyChanged("Items");
                }
                return _Items;
            }
            set {
                _Items = value;
                RaisePropertyChanged("Items");
            }
        }
        private ObservableCollection<T> _Items = null;


        /// <summary>
        /// Gets or sets the CurrentCharacter
        /// </summary>
        public T CurrentItem {
            get { return _CurrentItem; }
            set {
                _CurrentItem = value;
                OnCurrentItemChanged();
                if (value != null && typeof(BasePersistanceClass).IsAssignableFrom(value.GetType()))
                    value.Register();
                RaisePropertyChanged("CurrentItem");
            }
        }
        private T _CurrentItem;


        /// <summary>
        /// Gets or sets the NewCharacter
        /// </summary>
        public T NewItem {
            get {
                if (_NewItem == null)
                    _NewItem = CreateNewItem();
                return _NewItem;
            }
            set {
                _NewItem = value;
                RaisePropertyChanged("NewItem");
            }
        }
        private T _NewItem;

        /// <summary>
        /// Creates a new item
        /// </summary>
        /// <returns>a new item</returns>
        public virtual T CreateNewItem() {
            return new T() { Name = "New Item" };
        }

        /// <summary>
        /// Clears this service
        /// </summary>
        public void Clear() {
            Items = null;
            CurrentItem = null;
            NewItem = null;
        }

        /// <summary>
        /// Clears the new item
        /// </summary>
        public void ClearNewItem() {
            NewItem = null;
        }

        /// <summary>
        /// Save the new item and adds it to the collection of items after 
        /// which it resets the new item to null...ready for teh next use.
        /// </summary>
        public virtual void SaveNewItem() {
            Items.Add(this.NewItem);
            CurrentItem = Items[Items.IndexOf(this.NewItem)];
            DataContext.Store<T>(this.NewItem);
            ClearNewItem();
        }

        /// <summary>
        /// Deletes the current item
        /// </summary>
        public virtual void DeleteCurrentItem() {
            if (this.CurrentItem != null) {
                var temp = this.CurrentItem;

                this.Items.Remove(temp);
                DataContext.Delete<T>(temp);
            }
        }

        /// <summary>
        /// Saves the current item
        /// </summary>
        public virtual void SaveCurrentItem() {
            if (this.CurrentItem != null) {
                DataContext.Store<T>(this.CurrentItem);
            }
        }

        /// <summary>
        /// Fires when current item has changed
        /// </summary>
        public virtual void OnCurrentItemChanged() { }

        /// <summary>
        /// Raises the property changed event
        /// </summary>
        /// <param name="propertyName"></param>
        public void RaisePropertyChanged(string propertyName) {
            foreach (var handler in propertyChangedHandlers)
                handler.Method.Invoke(handler.Target, new object[] { this, new PropertyChangedEventArgs(propertyName) });
        }

        /// <summary>
        /// The property changed event
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged {
            add {
                propertyChangedHandlers.Add(value);
                BaseViewModel baseViewModel = value.Target as BaseViewModel;
                if (baseViewModel != null) {
                    baseViewModel.ViewUnLoaded += (s, e) => propertyChangedHandlers.Remove(value);
                }
            }
            remove {
                propertyChangedHandlers.Remove(value);
            }
        }

        private List<PropertyChangedEventHandler> propertyChangedHandlers = new List<PropertyChangedEventHandler>();


        private string fileName {
            get { return string.Format("{0}.txt", typeof(T).Name); }
        }

        private string fullName {
            get {
                return string.Format("C:\\DataBases\\{0}", fileName);
            }
        }

        /// <summary>
        /// Exports the information in this data service to a binary file.
        /// </summary>
        public virtual void Export() {
            FileInfo fInfo = new FileInfo(fullName);
            if (fInfo.Exists) fInfo.Delete();

            FileStream fileStream = new FileStream(this.fullName, FileMode.OpenOrCreate);
            XmlSerializer xs = new XmlSerializer(this.Items.GetType());
            xs.Serialize(fileStream, this.Items);

            var google = new GoogleService();
            google.UploadDocument(this.fullName);
        }

        /// <summary>
        /// Imports the items to this data service.
        /// </summary>
        public void Import() {
            FileInfo fInfo = new FileInfo(this.fullName);
            if (!fInfo.Exists) {
                if (new GoogleService().DownloadDocument(this.fileName)) {
                    FileInfo fInfo2 = new FileInfo(this.fullName);
                    if (!fInfo2.Exists) throw new Exception("The import does not exist.");
                }
                else {
                    throw new Exception("Something went wrong downloading the presets.");
                }
            }

            FileStream fileStream = new FileStream(this.fullName, FileMode.OpenOrCreate);
            XmlSerializer xs = new XmlSerializer(this.Items.GetType());
            var result = xs.Deserialize(fileStream);

            foreach (T item in result as IEnumerable<T>) {
                BasePersistanceClass temp = null;
                if (typeof(Statistic).IsAssignableFrom(typeof(T)))
                    temp = this.Items.FirstOrDefault(i => (i as Statistic).Level == (item as Statistic).Level);
                else
                    temp = this.Items.FirstOrDefault(i => i.Name == item.Name);

                if (temp != null) {
                    if (temp.TimeStamp == item.TimeStamp) break;
                    foreach (var pInfo in typeof(T).GetProperties()) {
                        temp.SetValue<object>(item.GetValue<object>(pInfo.Name), pInfo.Name);
                    }
                    DataContext.Store(temp);
                }
                else {
                    DataContext.Store(item);
                }
            }
            this.Clear();
        }
    }
}
